@glw907/cairn-cms 0.56.2 → 0.57.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/CHANGELOG.md +134 -0
  2. package/dist/components/AdminLayout.svelte +3 -0
  3. package/dist/components/CairnAdmin.svelte +8 -1
  4. package/dist/components/CairnAdmin.svelte.d.ts +2 -0
  5. package/dist/components/CairnMediaLibrary.svelte +949 -0
  6. package/dist/components/CairnMediaLibrary.svelte.d.ts +37 -0
  7. package/dist/components/EditPage.svelte +348 -7
  8. package/dist/components/EditPage.svelte.d.ts +2 -0
  9. package/dist/components/MarkdownEditor.svelte +283 -1
  10. package/dist/components/MarkdownEditor.svelte.d.ts +37 -1
  11. package/dist/components/MediaCaptureCard.svelte +135 -0
  12. package/dist/components/MediaCaptureCard.svelte.d.ts +40 -0
  13. package/dist/components/MediaFigureControl.svelte +247 -0
  14. package/dist/components/MediaFigureControl.svelte.d.ts +40 -0
  15. package/dist/components/MediaHeroField.svelte +578 -0
  16. package/dist/components/MediaHeroField.svelte.d.ts +75 -0
  17. package/dist/components/MediaInsertPopover.svelte +449 -0
  18. package/dist/components/MediaInsertPopover.svelte.d.ts +58 -0
  19. package/dist/components/MediaPicker.svelte +257 -0
  20. package/dist/components/MediaPicker.svelte.d.ts +41 -0
  21. package/dist/components/admin-icons.d.ts +12 -0
  22. package/dist/components/admin-icons.js +12 -0
  23. package/dist/components/cairn-admin.css +901 -9
  24. package/dist/components/client-ingest.d.ts +142 -0
  25. package/dist/components/client-ingest.js +297 -0
  26. package/dist/components/editor-media.d.ts +11 -0
  27. package/dist/components/editor-media.js +206 -0
  28. package/dist/components/editor-placeholder.d.ts +26 -0
  29. package/dist/components/editor-placeholder.js +166 -0
  30. package/dist/components/index.d.ts +1 -0
  31. package/dist/components/index.js +1 -0
  32. package/dist/components/markdown-directives.d.ts +12 -0
  33. package/dist/components/markdown-directives.js +42 -0
  34. package/dist/components/markdown-format.d.ts +89 -0
  35. package/dist/components/markdown-format.js +255 -0
  36. package/dist/components/media-upload-outcome.d.ts +52 -0
  37. package/dist/components/media-upload-outcome.js +48 -0
  38. package/dist/content/compose.js +3 -0
  39. package/dist/content/frontmatter.js +22 -0
  40. package/dist/content/manifest.d.ts +4 -0
  41. package/dist/content/manifest.js +41 -1
  42. package/dist/content/media-refs.d.ts +7 -0
  43. package/dist/content/media-refs.js +52 -0
  44. package/dist/content/schema.d.ts +5 -2
  45. package/dist/content/schema.js +17 -0
  46. package/dist/content/types.d.ts +64 -11
  47. package/dist/content/validate.js +31 -0
  48. package/dist/delivery/public-routes.d.ts +16 -0
  49. package/dist/delivery/public-routes.js +46 -3
  50. package/dist/delivery/seo-fields.js +7 -1
  51. package/dist/delivery/seo.d.ts +2 -0
  52. package/dist/delivery/seo.js +3 -0
  53. package/dist/doctor/checks-local.d.ts +1 -0
  54. package/dist/doctor/checks-local.js +21 -0
  55. package/dist/doctor/index.d.ts +3 -1
  56. package/dist/doctor/index.js +11 -2
  57. package/dist/doctor/types.d.ts +3 -0
  58. package/dist/doctor/wrangler-config.d.ts +3 -0
  59. package/dist/doctor/wrangler-config.js +20 -0
  60. package/dist/env.d.ts +19 -0
  61. package/dist/env.js +26 -0
  62. package/dist/index.d.ts +1 -1
  63. package/dist/log/events.d.ts +1 -1
  64. package/dist/media/config.d.ts +24 -0
  65. package/dist/media/config.js +69 -0
  66. package/dist/media/delivery-bucket.d.ts +34 -0
  67. package/dist/media/delivery-bucket.js +10 -0
  68. package/dist/media/index.d.ts +6 -0
  69. package/dist/media/index.js +13 -0
  70. package/dist/media/library-entry.d.ts +30 -0
  71. package/dist/media/library-entry.js +17 -0
  72. package/dist/media/manifest.d.ts +44 -0
  73. package/dist/media/manifest.js +105 -0
  74. package/dist/media/naming.d.ts +18 -0
  75. package/dist/media/naming.js +112 -0
  76. package/dist/media/reconcile.d.ts +36 -0
  77. package/dist/media/reconcile.js +45 -0
  78. package/dist/media/reference.d.ts +12 -0
  79. package/dist/media/reference.js +33 -0
  80. package/dist/media/sniff.d.ts +18 -0
  81. package/dist/media/sniff.js +106 -0
  82. package/dist/media/store.d.ts +25 -0
  83. package/dist/media/store.js +16 -0
  84. package/dist/media/transform-url.d.ts +26 -0
  85. package/dist/media/transform-url.js +38 -0
  86. package/dist/media/usage.d.ts +48 -0
  87. package/dist/media/usage.js +90 -0
  88. package/dist/render/pipeline.d.ts +2 -0
  89. package/dist/render/pipeline.js +13 -2
  90. package/dist/render/registry.js +3 -0
  91. package/dist/render/remark-figure.d.ts +4 -0
  92. package/dist/render/remark-figure.js +103 -0
  93. package/dist/render/resolve-media.d.ts +34 -0
  94. package/dist/render/resolve-media.js +78 -0
  95. package/dist/render/sanitize-schema.d.ts +4 -2
  96. package/dist/render/sanitize-schema.js +5 -3
  97. package/dist/sveltekit/admin-dispatch.d.ts +2 -0
  98. package/dist/sveltekit/admin-dispatch.js +5 -0
  99. package/dist/sveltekit/cairn-admin.d.ts +8 -1
  100. package/dist/sveltekit/cairn-admin.js +10 -2
  101. package/dist/sveltekit/content-routes.d.ts +77 -2
  102. package/dist/sveltekit/content-routes.js +470 -10
  103. package/dist/sveltekit/csrf.d.ts +16 -0
  104. package/dist/sveltekit/csrf.js +18 -0
  105. package/dist/sveltekit/guard.js +10 -3
  106. package/dist/sveltekit/index.d.ts +2 -1
  107. package/dist/sveltekit/index.js +1 -0
  108. package/dist/sveltekit/media-route.d.ts +12 -0
  109. package/dist/sveltekit/media-route.js +137 -0
  110. package/dist/vite/index.d.ts +3 -0
  111. package/dist/vite/index.js +7 -2
  112. package/package.json +7 -1
  113. package/src/lib/components/AdminLayout.svelte +3 -0
  114. package/src/lib/components/CairnAdmin.svelte +8 -1
  115. package/src/lib/components/CairnMediaLibrary.svelte +949 -0
  116. package/src/lib/components/EditPage.svelte +348 -7
  117. package/src/lib/components/MarkdownEditor.svelte +283 -1
  118. package/src/lib/components/MediaCaptureCard.svelte +135 -0
  119. package/src/lib/components/MediaFigureControl.svelte +247 -0
  120. package/src/lib/components/MediaHeroField.svelte +578 -0
  121. package/src/lib/components/MediaInsertPopover.svelte +449 -0
  122. package/src/lib/components/MediaPicker.svelte +257 -0
  123. package/src/lib/components/admin-icons.ts +12 -0
  124. package/src/lib/components/cairn-admin.css +37 -0
  125. package/src/lib/components/client-ingest.ts +380 -0
  126. package/src/lib/components/editor-media.ts +248 -0
  127. package/src/lib/components/editor-placeholder.ts +213 -0
  128. package/src/lib/components/index.ts +1 -0
  129. package/src/lib/components/markdown-directives.ts +46 -0
  130. package/src/lib/components/markdown-format.ts +307 -1
  131. package/src/lib/components/media-upload-outcome.ts +83 -0
  132. package/src/lib/content/compose.ts +3 -0
  133. package/src/lib/content/frontmatter.ts +20 -1
  134. package/src/lib/content/manifest.ts +44 -1
  135. package/src/lib/content/media-refs.ts +58 -0
  136. package/src/lib/content/schema.ts +31 -7
  137. package/src/lib/content/types.ts +80 -13
  138. package/src/lib/content/validate.ts +29 -1
  139. package/src/lib/delivery/public-routes.ts +52 -3
  140. package/src/lib/delivery/seo-fields.ts +6 -1
  141. package/src/lib/delivery/seo.ts +5 -0
  142. package/src/lib/doctor/checks-local.ts +22 -0
  143. package/src/lib/doctor/index.ts +21 -3
  144. package/src/lib/doctor/types.ts +3 -0
  145. package/src/lib/doctor/wrangler-config.ts +23 -0
  146. package/src/lib/env.ts +28 -0
  147. package/src/lib/index.ts +2 -0
  148. package/src/lib/log/events.ts +8 -1
  149. package/src/lib/media/config.ts +103 -0
  150. package/src/lib/media/delivery-bucket.ts +41 -0
  151. package/src/lib/media/index.ts +22 -0
  152. package/src/lib/media/library-entry.ts +58 -0
  153. package/src/lib/media/manifest.ts +122 -0
  154. package/src/lib/media/naming.ts +130 -0
  155. package/src/lib/media/reconcile.ts +79 -0
  156. package/src/lib/media/reference.ts +40 -0
  157. package/src/lib/media/sniff.ts +114 -0
  158. package/src/lib/media/store.ts +57 -0
  159. package/src/lib/media/transform-url.ts +58 -0
  160. package/src/lib/media/usage.ts +152 -0
  161. package/src/lib/render/pipeline.ts +17 -3
  162. package/src/lib/render/registry.ts +5 -0
  163. package/src/lib/render/remark-figure.ts +132 -0
  164. package/src/lib/render/resolve-media.ts +96 -0
  165. package/src/lib/render/sanitize-schema.ts +5 -3
  166. package/src/lib/sveltekit/admin-dispatch.ts +6 -1
  167. package/src/lib/sveltekit/cairn-admin.ts +13 -3
  168. package/src/lib/sveltekit/content-routes.ts +589 -12
  169. package/src/lib/sveltekit/csrf.ts +18 -0
  170. package/src/lib/sveltekit/guard.ts +12 -3
  171. package/src/lib/sveltekit/index.ts +6 -0
  172. package/src/lib/sveltekit/media-route.ts +158 -0
  173. package/src/lib/vite/index.ts +9 -2
package/CHANGELOG.md CHANGED
@@ -2,6 +2,140 @@
2
2
 
3
3
  All notable changes to this project are recorded here, most recent first.
4
4
 
5
+ ## 0.57.1
6
+
7
+ Media polish and cutover DX, the first follow-on after the `0.57.0` media stack. The Media Library
8
+ gains the action feedback it lacked: a delete, a rename, and a commit conflict now land on a strip
9
+ that confirms the result or shows the error, instead of a silent page. With the detail slide-over open
10
+ and focus in the search box, Escape now clears the search and leaves the panel open, rather than doing
11
+ both at once. A frontmatter hero marked decorative persists that choice as an additive `decorative` key
12
+ on the `image` object, so a deliberately decorative hero stops reading as needs-alt after a reload (a
13
+ decorative body image still cannot persist the choice, since markdown alt text has no slot for it). The
14
+ reserved-`figure` build error now names the colliding component and points at the fix.
15
+
16
+ The rest is documentation. The public media resolver wiring moved into the required media setup steps
17
+ in both the upgrade guide and the wire-the-delivery guide, since a published `media:` token ships bare
18
+ without it. The reserved-`figure` collision is now a prominent breaking callout. A new
19
+ [content authoring syntax reference](docs/reference/authoring-syntax.md) documents the `cairn:` and
20
+ `media:` token schemes together. The guides now show the `wrangler.toml` binding dialect, the
21
+ `@glw907/cairn-cms/media` import path, the empty-`media.json` bootstrap, and the `.site-main` re-scope
22
+ for the figure placement CSS.
23
+
24
+ No consumer action is required. The `decorative` key is additive and optional, so existing content
25
+ parses and builds unchanged, and the feedback strip, the Escape fix, and the registry error message
26
+ are admin or build-time with no public surface change.
27
+
28
+ ## 0.57.0
29
+
30
+ Images become first-class. An editor can paste, drag, or insert an image straight into a post, and
31
+ cairn stores it, names it by its content, commits it with the entry, and serves it from the site's
32
+ own R2 bucket. This is the whole media stack landing together: the foundation that models a stored
33
+ image, the infrastructure that ingests and delivers the bytes, and the insert UI that puts it in an
34
+ editor's hands. It is additive to the public API, but it needs per-site wiring, so it is a minor.
35
+
36
+ The foundation models an image as a logical reference, not a path. Content commits a `media:` token
37
+ keyed to the first 16 hex characters of the bytes' sha256, so the same image resolves no matter where
38
+ it is stored or what it is named, and identical bytes always land at one key. A small git-committed
39
+ manifest (`media.json`) carries the human layer the bytes cannot: the display name, the alt text, the
40
+ original filename, and the pixel facts. A render-time resolver reads that manifest and rewrites each
41
+ `media:` token to its delivery URL, optionally through a Cloudflare Images transform URL when a site
42
+ turns transforms on. The adapter's `AssetConfig` grew to declare the R2 bucket binding, the URL form,
43
+ the upload limits, and the named variants.
44
+
45
+ The infrastructure ingests and serves the bytes. A locked-down `/media` delivery route, built from
46
+ `createMediaRoute`, streams content-addressed bytes from R2: it validates the hash and extension
47
+ before any read, derives the object key from the validated values alone, carries the load-bearing
48
+ security headers (nosniff, inline disposition, a `default-src 'none'; sandbox` CSP, a one-year
49
+ immutable cache), and forwards `If-None-Match` and `Range` for 304 and 206 responses. An admin
50
+ `uploadAction` takes the editor's bytes, hashes them, dedups against the manifest with a put-first
51
+ head check, and rejects a hash collision with a 409. A client ingest helper normalizes a HEIC to a
52
+ web format before upload. A save merges the editor's optimistic records into `media.json` at commit
53
+ time, and the edit load hands the admin preview a lean `mediaTargets` projection so an in-session
54
+ image renders before it is committed.
55
+
56
+ The insert UI puts it in an editor's hands. Three gestures start an insert: paste from the clipboard,
57
+ drag a file onto the editor, or the toolbar's Insert image button. A paste or drag opens an at-caret
58
+ popover on the capture card with the dropped file; the button opens a chooser with upload first and a
59
+ combobox picker below it for reusing an image already on the site. The capture card pre-fills the name
60
+ from the filename and never blocks on alt text, so an editor can insert now and describe later. The
61
+ inserted reference renders in the editor as an atomic chip (thumbnail, name, and a needs-alt marker),
62
+ and an upload still in flight shows a widget-only placeholder with a determinate progress bar that
63
+ writes no document text until it resolves. A non-blocking needs-alt notice on the edit page counts the
64
+ images still waiting for a description and jumps to each one, never blocking a save or a Publish. The
65
+ edit-page preview renders inserted images through the same resolver the live site uses.
66
+
67
+ Figures land in the same release. An inline image can carry a caption and a placement through a
68
+ cairn-reserved `:::figure` directive that wraps the image as a child node. The caption is the
69
+ directive's body text, rendered to a real `<figcaption>`, and the placement is a closed role set
70
+ (`center`, `wide`, `full`, plus the bare measure default) carried as a class on the `<figure>`. A
71
+ persistent editor control wraps a bare image, edits an existing figure's caption and role, or unwraps
72
+ it, writing the markdown source the author can read and hand-edit, and the source chip shows the
73
+ figure's role so the decoration agrees with the source. `figure` and `figcaption` join the base
74
+ sanitize floor, so a captioned figure survives on every site, and `figure` is a reserved directive
75
+ name the registry refuses to let a site component shadow. cairn ships default `.cairn-place-*` CSS in
76
+ the showcase reference, and a site restyles those classes to own the placement pixels. A guide section
77
+ covers it in [add an image](docs/guides/add-an-image.md).
78
+
79
+ Hero images land in the same release. A Post or Page carries a lead image in frontmatter as a nested
80
+ `image: { src, alt, caption }` object, where `src` is a `media:` reference, `alt` is the screen-reader
81
+ description, and `caption` is an optional line the template may show. `image` is a new built-in field
82
+ type declared through `defineFields` like `text` or `date`. The editor renders it in the details panel
83
+ as a one-row resting field that opens the same picker and capture flow the body insert uses. Alt stays
84
+ debt, and the needs-alt notice now counts a hero with an empty alt alongside the body images. One
85
+ image serves two jobs: the delivery read path resolves the frontmatter reference into a derived
86
+ `heroImage` projection the template lays out, and the SEO head reads the same resolved image as the
87
+ `og:image` and `twitter:image`. The on-disk `media:` token stays canonical, since resolution is a
88
+ separate projection that is never written back. `resolveImageUrl` now rejects a non-http(s) result, so
89
+ an unresolved `media:` token degrades to no social image rather than shipping a broken tag. The site
90
+ template owns the hero layout: cairn ships the resolved data and the social-card wiring, not a hero
91
+ render step. A required `image` field is enforced on the presence of its `src`, never on its alt.
92
+
93
+ The Media Library lands in the same release. A first-class admin screen at `/admin/media`, a peer of
94
+ Posts and Pages, browses every committed asset, shows where each one is used, edits its name and
95
+ default alt, and deletes it safely. The resting surface is a contact-sheet grid with a list-density
96
+ toggle; a non-modal detail slide-over carries the preview, the alt editor, the grouped where-used
97
+ list, and the actions. The Library computes where-used by content hash across `main` and every open
98
+ edit branch, so a not-yet-published upload still shows and a renamed slug still resolves. The content
99
+ manifest gained an additive `mediaRefs` field per entry to feed the `main` side of that index; an
100
+ existing manifest without it still parses and builds. Safe-delete rechecks usage server-side against
101
+ a fresh read at delete time, refuses an in-use asset (the in-use face names what would break and
102
+ requires typing the slug), commits the manifest row removal before deleting the R2 object, and fails
103
+ closed if it cannot verify usage. Rename and default-alt are a single `media.json` row commit with no
104
+ reference rewrite, since the resolver and route key on the hash; the default alt is the value
105
+ prefilled into the next placement, not a rewrite of alt already committed. Replace, bulk actions, and
106
+ tags are deferred.
107
+
108
+ Consumers must: bind an R2 bucket and mount the delivery route before media works. Add an
109
+ `r2_buckets` binding named `MEDIA_BUCKET` in `wrangler.jsonc`, and mount the delivery route at
110
+ `src/routes/media/[...path]/+server.ts` with `createMediaRoute(runtime.resolvedAssets)`. Declare the
111
+ adapter's `assets` block naming that binding, and regenerate nothing else; media stays off until the
112
+ `assets` block is present. Cloudflare Images transforms stay behind the `transformations: false`
113
+ default, so a site serves full-size bytes until it opts in. The wiring steps are in
114
+ [the upgrade guide](docs/guides/upgrade-cairn.md) and the
115
+ [wire the delivery surface guide](docs/guides/wire-the-delivery-surface.md); the surface is documented
116
+ in [the media reference](docs/reference/media.md) and
117
+ [the sveltekit reference](docs/reference/sveltekit.md).
118
+
119
+ Consumers must also wire the public media resolver for any public image. The bucket, route, and
120
+ `assets` block make media work for the editor, but a published `![](media:...)` (a body image or a
121
+ frontmatter hero) ships a bare token to the live page unless the site threads a resolver into the
122
+ render path and `createPublicRoutes`. Build one with
123
+ `makeMediaResolver(mediaManifest, normalizeAssets({ bucketBinding: 'MEDIA_BUCKET' }))` from
124
+ `@glw907/cairn-cms/media`, where `mediaManifest` is the committed `src/content/.cairn/media.json`
125
+ (create it as `{}` on a fresh site so the import resolves). The
126
+ [upgrade guide](docs/guides/upgrade-cairn.md) gives the full snippet.
127
+
128
+ Breaking: `figure` is now a reserved directive name. `defineRegistry` throws if a site registers a
129
+ component named `figure`, which hard-fails both `cairn-manifest` and the build. A custom `figure` that
130
+ the engine's built-in figure now covers should be removed so the site adopts the engine's; a `figure`
131
+ that does something else should be renamed. Check too for any hand-authored `:::figure` block in your
132
+ content, which now renders as an engine figure.
133
+
134
+ Recommended, not required: regenerate the content manifest (`cairn-manifest`) and commit it so the
135
+ Media Library's `main` where-used is accurate. The `mediaRefs` field is additive, so a site builds
136
+ without it, but an un-regenerated manifest reads every published media reference as absent until it
137
+ is regenerated. Save and publish keep the field current from then on.
138
+
5
139
  ## 0.56.2
6
140
 
7
141
  The component insert picker gains a live preview and round-trip editing, and the component contract
@@ -20,6 +20,7 @@ identical on every host regardless of the site's own theme.
20
20
  import SignpostIcon from '@lucide/svelte/icons/signpost';
21
21
  import SettingsIcon from '@lucide/svelte/icons/settings';
22
22
  import UsersIcon from '@lucide/svelte/icons/users';
23
+ import ImageIcon from '@lucide/svelte/icons/image';
23
24
  import BlocksIcon from '@lucide/svelte/icons/blocks';
24
25
  import ExternalLinkIcon from '@lucide/svelte/icons/external-link';
25
26
  import './cairn-admin.css';
@@ -56,6 +57,8 @@ identical on every host regardless of the site's own theme.
56
57
  // the owner-only Editors.
57
58
  const coreItems: NavItem[] = $derived([
58
59
  ...data.concepts.map((c) => ({ label: c.label, icon: FileTextIcon, href: `/admin/${c.id}` })),
60
+ // Media is a content peer, immediately after the concepts.
61
+ { label: 'Media', icon: ImageIcon, href: '/admin/media' },
59
62
  ...(data.navLabel ? [{ label: data.navLabel, icon: SignpostIcon, href: '/admin/nav' }] : []),
60
63
  { label: 'Settings', icon: SettingsIcon, href: '/admin/settings' },
61
64
  ...(data.canManageEditors ? [{ label: 'Editors', icon: UsersIcon, href: '/admin/editors' }] : []),
@@ -13,11 +13,13 @@ mount inside `AdminLayout`. No styling or wrapper elements of its own.
13
13
  import EditPage from './EditPage.svelte';
14
14
  import ManageEditors from './ManageEditors.svelte';
15
15
  import NavTree from './NavTree.svelte';
16
+ import CairnMediaLibrary from './CairnMediaLibrary.svelte';
16
17
  import type { AdminData } from '../sveltekit/cairn-admin.js';
17
18
  import type { ContentFormFailure } from '../sveltekit/content-routes.js';
18
19
  import type { ComponentRegistry } from '../render/registry.js';
19
20
  import type { IconSet } from '../render/glyph.js';
20
21
  import type { LinkResolve } from '../content/links.js';
22
+ import type { MediaResolve } from '../render/resolve-media.js';
21
23
 
22
24
  interface Props {
23
25
  /** The discriminated view data from `createCairnAdmin`'s load. */
@@ -33,7 +35,10 @@ mount inside `AdminLayout`. No styling or wrapper elements of its own.
33
35
  })
34
36
  | null;
35
37
  /** The site's design-accurate render pipeline, for the edit view's preview pane. */
36
- render?: (md: string, opts?: { stagger?: boolean; resolve?: LinkResolve }) => string | Promise<string>;
38
+ render?: (
39
+ md: string,
40
+ opts?: { stagger?: boolean; resolve?: LinkResolve; resolveMedia?: MediaResolve },
41
+ ) => string | Promise<string>;
37
42
  /** The site's component registry, for the edit view's insert palette. */
38
43
  registry?: ComponentRegistry;
39
44
  /** The site's icon set, for the edit view's guided form fields. */
@@ -62,6 +67,8 @@ mount inside `AdminLayout`. No styling or wrapper elements of its own.
62
67
  <ManageEditors data={data.page} {form} />
63
68
  {:else if data.view === 'nav'}
64
69
  <NavTree data={data.page} />
70
+ {:else if data.view === 'media'}
71
+ <CairnMediaLibrary data={data.page} {form} />
65
72
  {/if}
66
73
  </AdminLayout>
67
74
  {/if}
@@ -3,6 +3,7 @@ import type { ContentFormFailure } from '../sveltekit/content-routes.js';
3
3
  import type { ComponentRegistry } from '../render/registry.js';
4
4
  import type { IconSet } from '../render/glyph.js';
5
5
  import type { LinkResolve } from '../content/links.js';
6
+ import type { MediaResolve } from '../render/resolve-media.js';
6
7
  interface Props {
7
8
  /** The discriminated view data from `createCairnAdmin`'s load. */
8
9
  data: AdminData;
@@ -18,6 +19,7 @@ interface Props {
18
19
  render?: (md: string, opts?: {
19
20
  stagger?: boolean;
20
21
  resolve?: LinkResolve;
22
+ resolveMedia?: MediaResolve;
21
23
  }) => string | Promise<string>;
22
24
  /** The site's component registry, for the edit view's insert palette. */
23
25
  registry?: ComponentRegistry;